%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tree.h"
#define YYERROR_VERBOSE 0
int yylex();
void yyerror(const char *s);
extern int lineno;
//TODO %union内无法使用typedef后的别名
%}
%union{
    enum yytokentype status;
    struct _expr_ *p_tree;
    struct _tree_table_ *p_tree_tab;
    struct _variable_ *p_var;
    union _values_ *p_val;
    char * p_str;
    struct _array_ *p_arr;
    unsigned char  p_num;
    unsigned short p_num2;
    unsigned long int p_num4;
    unsigned long long p_num8;
    double  p_double;
}

%token <p_str> NAME STRING


%token EQ NEQ GT LT GE LE IDENTITY NIDENTITY 

%left AND OR
//    ==,!=, >, <, >=,<=
%left EQ NEQ GT LT GE LE 
%left '='
%left '+' '-'
%left '*' '/'
%nonassoc UMINUS

/* 数据类型 */
%token NUM NUM2 NUM4 NUM8
%token BIT BIT8 BIT16 BIT32 BIT64
%token STR BOOLEAN INTEGER FLOAT
%token ARR DICTS POINTER NIL
%token <p_double> INT DECIMAL
/* 循环 */
%token DO WHILE FOR BREAK CONTINUE LOOP
/* 分支 */
%token IF ELSE ELSEIF SWITCH CASE DEFAULT
/* 函数 */
%token FUNC RETURN PRINT THEN END TO STRUCT

%type <status> accuracy data_type
%type <p_tree> assign_values expr func_call number
%type <p_tree_tab> params_list
%type <p_str> var_name
%type <p_num4> array array_num values2
%%

start: syntax_statement 
    | start syntax_statement
    ;
syntax_statement: body
    | def_function
    ;
body: def_var_syntax ';'
    | expr ';'
    ;
def_var_syntax: accuracy data_type NAME array { 
        //accuracy=INTEGER可用数据类型(NUM, NUM2, NUM4, NUM8);
        //accuracy=FLOAT可用数据类型(NUM4, NUM8);
        if($1==FLOAT && $2!=NUM4 && $2!=NUM8 ){
            yyerror("FLOAT data type It can only be NUM4 or NUM8!");
        }else if( ($2!=NUM && $2!=NUM2 && $2!=NUM4 && $2!=NUM8) && $1==INTEGER ){
            yyerror("invalid INTEGER!");
        }
        printf("array: %ld \n", $4);
        $<p_var>$ = createVariable($1, $2, $3, NULL);
    } assign_values { 
        
        tree *tmp_var = createTree(EXPR_DEF_VAR);
        tmp_var->entity.var_v=$<p_var>5;
        linkTree(tmp_var);
        
        if($6!=NULL){
            tree *tmp_tree = createTree(EXPR_ASSIGN);
            tmp_tree->entity.assign_v = createAssign(tmp_var, $6);
            linkTree(tmp_tree);
        }
        
    }
    ;
data_type: NUM { $$=NUM; }
    | NUM2 { $$=NUM2;}
    | NUM4 { $$=NUM4;}
    | NUM8 { $$=NUM8;}
    | BIT { $$=BIT;}
    | BIT8 { $$=BIT8;}
    | BIT16 { $$=BIT16;}
    | BIT32 { $$=BIT32;}
    | BIT64 { $$=BIT64;}
    | STR   { $$=STR;}
    | BOOLEAN { $$=BOOLEAN;}
    ;
assign_values: /* empty */ { $$=NULL;}
    | '=' expr { $$=$2; }
    | '=' array_val { }
    ;
values2: INT  { 
    // TODO 不能为0
    $$=$1; }
    ;
var_name : NAME { $$=$1; }
    | var_name '.'  NAME { }
    ;
accuracy: /* empty */ { $$=INTEGER; }
    | INTEGER { $$=INTEGER; }
    | FLOAT { $$=FLOAT; }
    ;
array: /* empty */ { $$=0; }
    | array_num { $$=$1;}
    ;
array_num: '[' values2 ']' { $$=$2; }
    | array_num '[' values2 ']' { $$=$1*$3; }
    ;
params_list:/* empty */ { }
    | expr { 
        //treeTable *tmp = createTreeTab();
        $$=linkTree2($1, NULL);
    }
    | params_list ',' expr { 
        $$=linkTree2($3, $1);
    }
    ;
func_call :  var_name '(' params_list ')' {
        $$=createTree(EXPR_CALL_FUNC);
        $$->entity.func_call_v = createFunccall($1, $3);
        linkTree($$);
    }
    ;
    
argv_list:/* empty */ { }
    | argv_list2 {  }
    ;
argv_list2: def_func_var {  }
    | argv_list ',' def_func_var {  }
    ;
def_func_var: data_type NAME array {}
    | INTEGER data_type NAME array {}
    | FLOAT data_type NAME array {}
    ;
def_function : FUNC NAME '(' argv_list ')' body END FUNC
    ;

expr: expr '-' expr 	{ $$=createTree(EXPR_BINARY);
        $$->entity.operation_v=createOperation(OP_SUB, $1, $3); }
	| expr '+' expr 	{ $$=createTree(EXPR_BINARY);
        $$->entity.operation_v=createOperation(OP_ADD, $1, $3); }
	| expr '*' expr 	{ $$=createTree(EXPR_BINARY);
        $$->entity.operation_v=createOperation(OP_MUL, $1, $3); }
	| expr '/' expr 	{ $$=createTree(EXPR_BINARY);
        $$->entity.operation_v=createOperation(OP_DIV, $1, $3); }
    | '-' number %prec UMINUS  {
        double tmp = $2->entity.var_v->value->f_number8->f;
        //转负数
        $2->entity.var_v->value->f_number8->f= -tmp;
        $$=$2;
    }
	| '(' expr ')' {
        $2->entity.operation_v->prec=1;
        $$=$2;
    } 
    | var_name array { 
        variable *temp = (variable *)malloc(sizeof(variable));
        temp->name=strdup($1);
        temp->attr=ATTR_VAR;

        if($2) temp->arr=NULL;

        $$=createTree(EXPR_VAR_CONSTANT);
        $$->entity.var_v = temp;
    }
    | number { $$=$1; }
    | STRING { 
        values *tmp_val = createValues(DATA_TYPE_STRING, $1); 
        $$=createConstant(DATA_TYPE_STRING, tmp_val, NULL);}
    | func_call { $$=$1; }
    ;
number: INT {
        values *val = createValuesFloat($1); 
        $$=createConstant(DATA_TYPE_F_NUMBER_8, val, NULL);
        $$->entity.var_v->is_float = 0;
    }
    | DECIMAL {
        values *val = createValuesFloat($1); 
        $$=createConstant(DATA_TYPE_F_NUMBER_8, val, NULL);
        $$->entity.var_v->is_float = 1;
    }
array_val: '[' array_val_2 ']'
    ;
array_val_2: array_val_list
    | '[' array_val_2 ']'
    ;
array_val_list: expr { }
    | array_val_list ',' expr { }
    ;
%%



void yyerror(const char *s){
    fprintf(stdout, "line %d: %s \n", lineno, s);
    exit(0);
}
int yywrap(){
    return 1;
}
extern FILE * yyin;
extern void INIT();
int main (int argc, char *argv[])
{
    /*
    lex l_lang.l
    yacc -d y_lang.y
    gcc -o demo y.tab.c lex.yy.c tree.c map.c asm.c parser.c -ll -lm

    编译成可执行程序
    nasm -f elf64 -o PLang.o PLang.asm
    ld -s -e main -o PLang PLang.o sys_func.o

    编译链接对象
    gcc -c sys_func.c

    链接可执行程序
    gcc -fno-pic -static -fno-builtin -fno-strict-aliasing -fno-omit-frame-pointer -fno-stack-protector -fno-pie -no-pie PLang.o sys_func.o
    */
    yyin = fopen(argv[1], "r");
    yyparse();
    INIT();
    fclose(yyin);
    return 0;
}